import { math, Vec2 } from 'cc';
import { ecsclass, EcsComponent, ITypeofComponent } from 'db://pkg/@gamex/cc-ecs';
import { NodeComponent } from './NodeComponent';

interface IOptions {
    /**
     * 允许旋转
     * @description 默认值: false
     */
    openRotate: boolean;
    /**
     * 追击目标
     * @description 默认值: null
     */
    target: NodeComponent;
    /**
     * 初始角度
     * @description 单位: 度
     * @description 默认值: 0
     * @description 取值范围: (-180, 180]
     */
    toward: number;
    /**
     * 速度
     * @description 单位: 像素/秒
     * @description 默认值: 0
     * @description 取值范围: (-oo, +oo)
     */
    speed: number;
    /**
     * 最小速度
     * @description 单位: 像素/秒
     * @description 默认值: -Infinity
     * @description 取值范围: (-oo, +oo)
     */
    minSpeed: number;
    /**
     * 最大速度
     * @description 单位: 像素/秒
     * @description 默认值: Infinity
     * @description 取值范围: (-oo, +oo)
     */
    maxSpeed: number;
    /**
     * 加速度
     * @description 单位: 像素/秒
     * @description 默认值: 0
     * @description 取值范围: (-oo, +oo)
     */
    acceleration: number;
    /**
     * 转向角度(0表示无穷大)
     * @description 单位: 度/秒
     * @description 默认值: 180
     * @description 取值范围: [0, +oo)
     */
    turnAngle: number;
    /**
     * 偏航角度
     * @description 单位: 度
     * @description 默认值: 0
     * @description 取值范围: (-180, 180]
     */
    deviation: number;
    /**
     * 修正角度延迟
     * @description 单位: 秒
     * @description 默认值: 0
     * @description 取值范围: [0, +oo)
     */
    correctDelay: number;
    /**
     * 延迟阶段速度(会覆盖speed)
     * @description 单位: 像素/秒
     * @description 默认值: 0
     * @description 取值范围: [0, +oo)
     */
    speedInDelay: number;
    /**
     * 延迟阶段最小速度
     * @description 单位: 像素/秒
     * @description 默认值: -Infinity
     * @description 取值范围: (-oo, +oo)
     */
    minSpeedInDelay: number;
    /**
     * 延迟阶段最大速度
     * @description 单位: 像素/秒
     * @description 默认值: Infinity
     * @description 取值范围: (-oo, +oo)
     */
    maxSpeedInDelay: number;
    /**
     * 延迟阶段加速度
     * @description 单位: 像素/秒
     * @description 默认值: 0
     * @description 取值范围: (-oo, +oo)
     */
    accelerationInDelay: number;
}

export interface IMoveComponent extends IOptions {
    /**
     * 内置字段，由系统访问
     * @description 单位: 度
     * @description 默认值: 0
     * @description 取值范围: (-180, 180]
     */
    angle: number;
    /**
     * 内置字段，由系统访问
     * @description 单位: 像素
     * @description 默认值: 0
     * @description 取值范围: [0, +oo)
     */
    distance: number;
}

/**
 * 移动方向(只给出了常用的方向)
 * @description 单位: 度
 * @description 取值范围: (-180, 180]
 */
export enum MoveTowardType {
    Right = 0,
    RightUp = 45,
    Up = 90,
    LeftUp = 135,
    Left = 180,
    LeftDown = -135,
    Down = -90,
    RightDown = -45
}

@ecsclass('MoveComponent')
export class MoveComponent extends EcsComponent implements IMoveComponent {
    static requires: ITypeofComponent[] = [NodeComponent];
    static allowRecycling: boolean = true;
    protected onDisable() {
        this._angle = null;
        this._distance = 0;
        this.openRotate = false;
        this._targetUUID = null;
        this._target = null;
        this._toward = null;
        this._speed = 0;
        this._minSpeed = -Infinity;
        this._maxSpeed = Infinity;
        this._acceleration = 0;
        this._turnAngle = 180;
        this._deviation = 0;
        this._correctDelay = 0;
        this._speedInDelay = 0;
        this._minSpeedInDelay = -Infinity;
        this._maxSpeedInDelay = Infinity;
        this._accelerationInDelay = 0;
    }

    ////////////////////内置字段////////////////////////
    private _angle: number = null;
    public get angle() {
        if (this._angle === null) {
            this._angle = MoveComponent.getAngleFromNeg180to180(this.deviation + this.toward);
        }
        return this._angle;
    }
    private set angle(value) {
        this._angle = MoveComponent.getAngleFromNeg180to180(value);
    }

    private _distance = 0;
    public get distance() {
        return this._distance;
    }
    private set distance(value) {
        this._distance = value;
    }

    ////////////////////配置字段////////////////////////
    public openRotate = false;

    private _targetUUID: number = null;
    private _target: NodeComponent = null;
    public get target(): NodeComponent {
        if (this._target && this._target.uuid !== this._targetUUID) {
            this._target = null;
        }
        return this._target;
    }
    public set target(value: NodeComponent) {
        this._target = (value && value.isValid) ? value : null;
        if (this._target) {
            this._targetUUID = this._target.uuid;
        } else {
            this._targetUUID = null;
        }
        if (this._target && this._toward === null) {
            this._toward = MoveComponent.getTowardAngle(this.entity.getComponent(NodeComponent), this.target);
        }
    }

    private _toward: number = null;
    public get toward(): number {
        return this._toward;
    }
    public set toward(value: number) {
        this._toward = MoveComponent.getAngleFromNeg180to180(value);
    }

    private _speed: number = 0;
    public get speed(): number {
        return this._speed;
    }
    public set speed(value: number) {
        this._speed = value;
    }

    private _minSpeed: number = -Infinity;
    public get minSpeed(): number {
        return this._minSpeed;
    }
    public set minSpeed(value: number) {
        this._minSpeed = value;
    }

    private _maxSpeed: number = Infinity;
    public get maxSpeed(): number {
        return this._maxSpeed;
    }
    public set maxSpeed(value: number) {
        this._maxSpeed = value;
    }

    private _acceleration: number = 0;
    public get acceleration(): number {
        return this._acceleration;
    }
    public set acceleration(value: number) {
        this._acceleration = value;
    }

    private _turnAngle: number = 180;
    public get turnAngle(): number {
        return this._turnAngle;
    }
    public set turnAngle(value: number) {
        this._turnAngle = Math.max(value, 0);
    }

    private _deviation: number = 0;
    public get deviation(): number {
        return this._deviation;
    }
    public set deviation(value: number) {
        this._deviation = MoveComponent.getAngleFromNeg180to180(value);
    }

    private _correctDelay: number = 0;
    public get correctDelay(): number {
        return this._correctDelay;
    }
    public set correctDelay(value: number) {
        this._correctDelay = Math.max(value, 0);
    }

    private _speedInDelay: number = 0;
    public get speedInDelay(): number {
        return this._speedInDelay;
    }
    public set speedInDelay(value: number) {
        this._speedInDelay = value;
    }

    private _minSpeedInDelay: number = -Infinity;
    public get minSpeedInDelay(): number {
        return this._minSpeedInDelay;
    }
    public set minSpeedInDelay(value: number) {
        this._minSpeedInDelay = value;
    }

    private _maxSpeedInDelay: number = Infinity;
    public get maxSpeedInDelay(): number {
        return this._maxSpeedInDelay;
    }
    public set maxSpeedInDelay(value: number) {
        this._maxSpeedInDelay = value;
    }

    private _accelerationInDelay: number = 0;
    public get accelerationInDelay(): number {
        return this._accelerationInDelay;
    }
    public set accelerationInDelay(value: number) {
        this._accelerationInDelay = value;
    }

    /**
     * 获取self朝向other的角度
     */
    static getTowardAngle(self: NodeComponent, other: NodeComponent) {
        if (!self) return 0;
        if (!other) return 0;
        return math.toDegree(Math.atan2(other.y - self.y,
            other.x - self.x));
    }

    /**
     * 修正为范围值
     * Range
     */
    static getAbsoluteValue(input: number, threshold: number) {
        if (threshold < 0) {
            threshold = -threshold;
        }
        if (input > 0) {
            if (input > threshold) return threshold;
            return input;
        }
        if (input < 0) {
            if (input < -threshold) return -threshold;
        }
        return input;
    }

    /**
     * 修正到(-180, 180]
     */
    static getAngleFromNeg180to180(angle: number) {
        if (angle >= 360 || angle <= -360) {
            angle = angle % 360;
        }
        if (angle > 180) {
            angle -= 360;
        } else if (angle <= -180) {
            angle += 360;
        }
        return angle;
    }

    /**
     * 修正到[0, 360)
     */
    static getAngleFrom0To360(angle: number) {
        if (angle >= 360 || angle <= -360) {
            angle = angle % 360;
        }
        if (angle < 0) {
            angle += 360;
        }
        return angle;
    }

    /**
     * 修正到(-360, 0]
     */
    static getAngleFromNeg360To0(angle: number) {
        if (angle >= 360 || angle <= -360) {
            angle = angle % 360;
        }
        if (angle > 0) {
            angle -= 360;
        }
        return angle;
    }

    /**
     * 根据速度二维分量获取toward值
     */
    static getTowardByVelocity(velocity: Vec2) {
        // y速度为0
        if (velocity.y === 0) {
            if (velocity.x >= 0) {
                return MoveTowardType.Right;
            } else {
                return MoveTowardType.Left;
            }
        }
        // x速度为0
        if (velocity.x === 0) {
            if (velocity.y >= 0) {
                return MoveTowardType.Up;
            } else {
                return MoveTowardType.Down;
            }
        }
        // x、y速度相等
        if (velocity.x === velocity.y) {
            if (velocity.x >= 0) {
                return MoveTowardType.RightDown;
            } else {
                return MoveTowardType.LeftDown;
            }
        }
        // x、y速度相反
        if (velocity.x === -velocity.y) {
            if (velocity.x >= 0) {
                return MoveTowardType.RightUp;
            } else {
                return MoveTowardType.LeftUp;
            }
        }

        return Vec2.UNIT_X.signAngle(velocity);
    }
}